package com.supsi.segreteria.client;

/**
 * Created with IntelliJ IDEA.
 * User: cristian
 * Date: 5/4/12
 * Time: 4:47 PM
 * To change this template use File | Settings | File Templates.
 */

import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Properties;

public class Configurator {


    /**
     * Singleton instance
     */
    private static Configurator instance = null;

    /**
     *  server address
     */
    private String fServerAddress = "";

    /**
     *  server port
     */
    private int fServerPort = 0;

    /**
     * user name
     */
    private String fUserName = "";

    /**
     * user id number
     */
    private long fUserNumber = 0;

    /**
     * configuration property file name
     */
    private String fConfigFileName = "com.supsi.segreteria.client.properties";

    /**
     * address book property file
     */
    private String fAddressBookFileName ="com.supsi.segreteria.addressbook.properties";

    /**
     * config property container
     */
    private Properties fConfigProperties;

    /**
     * addressbook property container
     */
    private Properties fAddressBookProperties;

    /**
     * AddressBookEntry Class used to manage addressbook entries
     */
    public class AddressBook{

        /**
         * our address book entry structure
         */
        public class AddressBookEntry{
            private Long fUserNumber;
            private String fUserName;

            /**
             * public constructor
             * @param newUserNumber Long user number
             * @param newUserName String new user name
             */
            public AddressBookEntry(Long newUserNumber, String newUserName){
                this.fUserName = newUserName;
                this.fUserNumber = newUserNumber;
            }

            /**
             * getter
             * @return Long user number
             */
            public Long getUserNumber(){
                return fUserNumber;
            }

            /**
             * getter
             * @return String user name
             */
            public String getUserName(){
                return fUserName;
            }


            @Override
            public String toString(){
                return "["+fUserNumber+":"+fUserName+"]";
            }

            @Override
            public int hashCode(){
                return this.hashCode();
            }

            @Override
            public boolean equals(Object obj){

                //null object
                if (obj == null)
                    return false;

                //same object
                if (obj == this)
                    return true;

                //different class
                if (obj.getClass() != getClass())
                    return false;

                //let's cast it to a proper value and then test content
                AddressBookEntry ab = (AddressBookEntry) obj;
                if (this.getUserName().equals(ab.getUserName()))
                    if (this.getUserNumber().equals(ab.getUserNumber()))
                        return true;

                //if nothing equals above, the return false
                return false;

            }
        }

        /**
         * storage for entries
         */
        private ArrayList<AddressBookEntry> fAddressBookEntries = new ArrayList<AddressBookEntry>();

        /**
         * return number of elements within the addressbook
         * @return int number of elements
         */
        public int size(){
            return fAddressBookEntries.size();
        }

        /**
         * returns an array of Addressbook entries
         * @return AddressBookEntry[]
         */
        public AddressBookEntry[] getAddresses(){
            AddressBookEntry abe[] = new AddressBookEntry[fAddressBookEntries.size()];
            abe = fAddressBookEntries.toArray(abe);
            return abe;
        }

        /**
         * add new entry to addressbook
         * @param newUserNumber Long new user number
         * @param newUserName   String new user name
         */
        public void addEntry(Long newUserNumber, String newUserName ){
            fAddressBookEntries.add(new AddressBookEntry(newUserNumber,newUserName));
        }

        public void removeEntry(long userNumber, String username){
            int idx = fAddressBookEntries.indexOf(new AddressBookEntry(userNumber,username));
            if (idx > -1)
                fAddressBookEntries.remove(idx);
        }

        public String getUserName(String userNumber){

            String userName = userNumber;
            Long userNum;

            if(isNumber(userNumber)){
                userNum = Long.parseLong(userNumber);
                //if found then set real user name otherwise its number is already set
                for ( AddressBook.AddressBookEntry entry: addressBook.getAddresses()){
                    //set property values
                    if (entry.getUserNumber().intValue() == userNum.intValue()){
                        userName = entry.getUserName();
                        break;
                    }
                }
            }


            return userName;
        }
    }

    /**
     * new local addressbook
     */
    public AddressBook addressBook = new AddressBook();


    //getter & setters

    /**
     * configuration file type
     */
    public enum eConfigFileType {
        Client, AddressBook
    }

    /**
     *
     * @return fServerAddress Sergreteria server address
     */
    public String getServerAddress() {
        return fServerAddress;
    }

    /**
     *
     * @param fServerAddress set Segreteria server address
     */
    public void setServerAddress(String fServerAddress) {
        this.fServerAddress = fServerAddress;
    }

    /**
     * return server port
     * @return int port
     */
    public int getServerPort(){
        return fServerPort;
    }

    /**
     * set server port
     * @param newPort int server port
     */
    public void setServerPort(int newPort){
        fServerPort = newPort;
    }

    /**
     *
     * @return return user name
     */
    public String getfUserName() {
        return fUserName;
    }

    /**
     *
     * @param fUserName set username
     */
    public void setfUserName(String fUserName) {
        this.fUserName = fUserName;
    }

    /**
     *
     * @return return user number
     */
    public long getfUserNumber() {
        return fUserNumber;
    }

    /**
     *
     * @param fUserNumber set user number
     */
    public void setfUserNumber(long fUserNumber) {
        this.fUserNumber = fUserNumber;
    }

    /**
     * class constructor  --> private because SingleTon.
     */
    private Configurator(){

        fConfigProperties = new Properties();
        fAddressBookProperties = new Properties();
        loadConfigFiles();

    }

    /**
     * load configuration files
     */
    private void loadConfigFiles() {

        String addressBookKey;
        String addressBookKeyValues[];
        String addressBookValue;

        //load client system properties
        try {
            fConfigProperties.load(new FileInputStream(fConfigFileName));
            fServerAddress = fConfigProperties.getProperty("serveraddress");
            fServerPort = Integer.parseInt(fConfigProperties.getProperty("serverport"));
            fUserName = fConfigProperties.getProperty("username");
            fUserNumber = Long.parseLong(fConfigProperties.getProperty("usernumber"));

        } catch (FileNotFoundException fnfException){
            System.out.println(this.getClass().getName()+" Client configuration file <"+fConfigFileName +"> not found.");
            System.out.println(this.getClass().getName()+" -->generating a new config file with default values");
            generateDefaultConfig(eConfigFileType.Client);
        } catch (IOException ioException) {
            System.out.println(this.getClass().getName()+" -->errors loading <"+fConfigFileName +">");
            System.out.println(this.getClass().getName()+" -->generating a new config file with default values");
            generateDefaultConfig(eConfigFileType.Client);
        }

        //load client Addressbook properties
        try {
            fAddressBookProperties.load(new FileInputStream(fAddressBookFileName));
            for (Enumeration e = fAddressBookProperties.keys();e.hasMoreElements();) {
                addressBookKey = (String) e.nextElement();
                addressBookKeyValues = addressBookKey.split(":");
                addressBookValue = (String) fAddressBookProperties.get(addressBookKey);
                addressBook.addEntry(Long.parseLong(addressBookKeyValues[1]),addressBookValue);
            }

        } catch (FileNotFoundException fnfException){
            System.out.println(this.getClass().getName()+" Addressbook configuration file <"+fAddressBookFileName +"> not found.");
            System.out.println(this.getClass().getName()+" -->generating a new empty addressbook");
            generateDefaultConfig(eConfigFileType.AddressBook);
        } catch (IOException ioException) {
            System.out.println(this.getClass().getName()+" -->errors loading <"+fAddressBookFileName +">");
            System.out.println(this.getClass().getName()+" -->generating a new empty addressbook");
            generateDefaultConfig(eConfigFileType.AddressBook);
        }
    }

    /**
     * generate and save default config file
     * @param type Client or AddressBook
     */
    private void generateDefaultConfig(eConfigFileType type){

        switch (type){

            case Client:
                //set default values
                fServerAddress = "127.0.0.1";
                fServerPort = 9090;
                fUserName = "Doe, John";
                fUserNumber = 0;

                saveConfig();

                break;

            case AddressBook:

                //set default values
                addressBook.addEntry(new Long(0),"Doe, John");

                saveAddressBook();

                break;
        }
    }

    /**
     * save config file
     */
    public void saveConfig(){
        //set property values
        fConfigProperties.setProperty("serveraddress",fServerAddress);
        fConfigProperties.setProperty("serverport",new Integer(fServerPort).toString());
        fConfigProperties.setProperty("username",fUserName);
        fConfigProperties.setProperty("usernumber",new Long(fUserNumber).toString());

        //try to save to file
        try{
            fConfigProperties.store(new FileOutputStream(fConfigFileName), "Segreteria Client version 1.0");
        } catch (FileNotFoundException fnfException ){
            System.err.println(this.getClass().getName()+"Error creating configuration file <"+fConfigFileName +">");
        } catch(IOException ioException){
            System.err.println(this.getClass().getName()+"Error writing to configuration file <"+fConfigFileName +">");
        }

    }

    /**
     * save addressbook to disk
     */
    public void saveAddressBook(){

        //clear property container
        fAddressBookProperties.clear();

        //iterate through all addresses
        for ( AddressBook.AddressBookEntry entry: addressBook.getAddresses()){
            //set property values
            fAddressBookProperties.setProperty("ab-entry:"+entry.getUserNumber(),entry.getUserName());
        }

        //try to save file or generate a new one
        try{
            fAddressBookProperties.store(new FileOutputStream(fAddressBookFileName), "Segreteria AddressBoook version 1.0");
        } catch (FileNotFoundException fnfException ){
            System.err.println(this.getClass().getName()+"Error creating Addressbook file <"+fAddressBookFileName +">");
        } catch(IOException ioException){
            System.err.println(this.getClass().getName()+"Error writing to Addressbook file <"+fAddressBookFileName +">");
        }

    }

    /**
     * get singleton instance
     */
    public static Configurator getInstance(){
        if (instance == null)
            instance = new Configurator();

        return instance;
    }

    /**
     * check if a string contains a number
     *
     * @param strNumber String value to be checked if number
     * @return boolean true if string contains a number
     */
    public boolean isNumber(String strNumber) {
        try {
            Long.parseLong(strNumber.trim());
            return true;
        } catch (NumberFormatException nfe) {
        }
        return false;
    }

}
